package com.yang.biz.modular.backup.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.cron.CronUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yang.common.enums.CommonSortOrderEnum;
import com.yang.common.exception.CommonException;
import com.yang.common.page.CommonPageRequest;
import com.yang.common.util.CommonFormatUtil;
import com.yang.common.util.CommonServletUtil;
import com.yang.biz.core.constant.DevConstant;
import com.yang.biz.modular.backup.entity.BackupRecord;
import com.yang.biz.modular.backup.enums.BackupTypeEnum;
import com.yang.biz.modular.backup.mapper.BackupMapper;
import com.yang.biz.modular.backup.param.*;
import com.yang.biz.modular.backup.result.BackupConfigResult;
import com.yang.biz.modular.backup.service.BackupService;
import com.yang.biz.modular.backup.task.BackupDbTaskRunner;
import com.yang.common.util.CommonCommandUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.support.CronExpression;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

/**
 * @author: yangjianzhi
 * @version1.0
 */
@Slf4j
@Service
public class BackupServiceImpl extends ServiceImpl<BackupMapper, BackupRecord> implements BackupService {

    private static final String DB_USERNAME_KEY = "spring.datasource.username";

    private static final String DB_PASSWORD_KEY = "spring.datasource.password";

    @Resource
    private Environment environment;

    private void checkParam(BackupConfigParam backupConfigParam) {
        if (!StrUtil.isAllNotBlank(backupConfigParam.getPath(), backupConfigParam.getFrequency())) {
            throw new CommonException("备份路径和周期不可为空!");
        }
        if (!CronExpression.isValidExpression(backupConfigParam.getFrequency())) {
            throw new CommonException("cron表达式：{}格式不正确", backupConfigParam.getFrequency());
        }
    }

    @Override
    public QueryWrapper<BackupRecord> getQueryWrapper(BackupQueryParam backupQueryParam) {
        QueryWrapper<BackupRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().orderByDesc(BackupRecord::getId);
        return queryWrapper;
    }

    @Override
    public Page<BackupRecord> page(BackupPageParam backupPageParam) {
        QueryWrapper<BackupRecord> queryWrapper = getQueryWrapper(BeanUtil.toBean(backupPageParam, BackupQueryParam.class));
        if (ObjectUtil.isAllNotEmpty(backupPageParam.getSortField(), backupPageParam.getSortOrder())) {
            CommonSortOrderEnum.validate(backupPageParam.getSortOrder());
            queryWrapper.orderBy(true, backupPageParam.getSortOrder().equals(CommonSortOrderEnum.ASC.getValue()),
                    StrUtil.toUnderlineCase(backupPageParam.getSortField()));
        } else {
            queryWrapper.lambda().orderByDesc(BackupRecord::getId);
        }
        return this.page(CommonPageRequest.defaultPage(), queryWrapper);
    }

    @Override
    public List<BackupRecord> list(BackupListParam backupListParam) {
        QueryWrapper<BackupRecord> queryWrapper = getQueryWrapper(BeanUtil.toBean(backupListParam, BackupQueryParam.class));
        return this.list(queryWrapper);
    }

    @Override
    public BackupRecord queryEntity(String id) {
        BackupRecord backupInfo = this.getById(id);
        if (ObjectUtil.isEmpty(backupInfo)) {
            throw new CommonException("备份信息不存在，id值为：{}", id);
        }
        return backupInfo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void restore(String backupId) {
        BackupRecord backupInfo = this.queryEntity(backupId);
        // todo: 备份配置
        BackupConfigResult backupDbParam = new BackupConfigResult();
        String backupFullPath = backupDbParam.getBackupPath() + File.separator + backupInfo.getBackupName();

        try {
            CommonCommandUtil.restoreDb(backupDbParam.getDbHost(), backupDbParam.getDbPort(), backupDbParam.getDbName(), environment.getProperty(DB_USERNAME_KEY)
                    , environment.getProperty(DB_PASSWORD_KEY), backupFullPath);
        } catch (IOException | InterruptedException exception) {
            throw new CommonException("还原数据库备份失败!");
        }
    }

    @Override
    public void backup(String backupType) {
        // todo: 备份配置
        BackupConfigResult backupConfigResult = new BackupConfigResult();
        backupConfigResult.setDbHost("127.0.0.1");
        backupConfigResult.setDbName("snwoy-starter");
        backupConfigResult.setDbPort("3306");
        backupConfigResult.setBackupFrequency("0 0/2 * * * ?");
        backupConfigResult.setBackupPath("D:\\work");
        String backupName = backupConfigResult.getBackupName();
        String backupFullPath = backupConfigResult.getBackupPath() + File.separator + backupName;

        try {
            CommonCommandUtil.backupDb(backupConfigResult.getDbHost(), backupConfigResult.getDbPort(), environment.getProperty(DB_USERNAME_KEY)
                    , environment.getProperty(DB_PASSWORD_KEY), backupConfigResult.getDbName(), backupFullPath);
            // 添加备份记录
            long size = FileUtil.size(new File(backupFullPath));
            BackupAddParam backupAddParam = new BackupAddParam();
            backupAddParam.setBackupName(backupName);
            backupAddParam.setBackupType(backupType);
            backupAddParam.setDataSize(CommonFormatUtil.formatFileSize(size));
            backupAddParam.setBackupPath(backupFullPath);

            if (StrUtil.isNotBlank(backupAddParam.getBackupName()) && ObjectUtil.isNotEmpty(backupAddParam.getDataSize())) {
                BackupRecord backupInfo = BeanUtil.toBean(backupAddParam, BackupRecord.class);
                backupInfo.setBackupTime(DateUtil.now());
                this.save(backupInfo);
            }
        } catch (Exception exception) {
            throw new CommonException("备份失败!");
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void performManual() {
        this.backup(BackupTypeEnum.MANUAL.getValue());
    }

    /**
     * 开始自动备份任务（根据实时配置信息）
     */
    @Override
    public void restartJob() {
        // todo: 备份配置
        BackupConfigResult backupConfigResult = new BackupConfigResult();
        String backupFrequency = backupConfigResult.getBackupFrequency();

        if (!CronExpression.isValidExpression(backupFrequency)) {
            throw new CommonException("cron表达式：{}格式不正确", backupFrequency);
        }

        CronUtil.remove(DevConstant.BACKUP_JOB_KEY);
        if (StrUtil.isNotBlank(backupFrequency)) {
            CronUtil.schedule(DevConstant.BACKUP_JOB_KEY, backupFrequency, () -> {
                SpringUtil.getBean(BackupDbTaskRunner.class).action();
            });
        }
    }

    @Override
    public void download(String backupId) {
        BackupRecord backupRecord = this.queryEntity(backupId);
        String backupPath = backupRecord.getBackupPath();
        if (StrUtil.isBlank(backupPath)) {
            throw new CommonException("该备份记录缺少备份路径");
        }
        File file = new File(backupPath);
        if (file.exists() && file.isFile()) {
            ServletUtil.write(CommonServletUtil.getResponse(), file);
        } else {
            throw new CommonException("该备份路径不存在：{}", backupPath);
        }
    }
}
